#ifndef TB_REFLECTION_SERIALIZER_H__
#define TB_REFLECTION_SERIALIZER_H__

#include "../reflection/transform.hpp"
#include "../reflection/reflection.hpp"
#include "../reflection/type_traits.hpp"
#include "../reflection/type_info.hpp"
#include "../reflection/collection_enumer.hpp"

TB_NAMESPACE_BEGIN

template <typename T, typename Enabled = void> struct 
is_writer 
   : false_type {};

template <typename T> struct 
is_writer<T, typename enable_if<check_method<void (T::*)(const reflection::TypeInfo*, uint32), &T::WriteStructBegin>::value >::type >
  : true_type {};

template <class Writer>
struct SimpleTypeSerializerTrait {
  static bool WriteValue(Writer& w, uint32 typeId, const Variant& val) {

#define SERIALIZE_SIMPLE_TYPE_IMPL(T) \
      w.WriteValue(val.Value<T>());\
      return true;

    switch (typeId) {
    case Variant::kEnumType:
    case Variant::kInt32Type:
      SERIALIZE_SIMPLE_TYPE_IMPL(int32);
    case Variant::kBoolType:
      SERIALIZE_SIMPLE_TYPE_IMPL(bool);
    case Variant::kInt8Type:
      SERIALIZE_SIMPLE_TYPE_IMPL(int8);
    case Variant::kUint8Type:
      SERIALIZE_SIMPLE_TYPE_IMPL(uint8);
    case Variant::kInt16Type:
      SERIALIZE_SIMPLE_TYPE_IMPL(int16);
    case Variant::kUint16Type:
      SERIALIZE_SIMPLE_TYPE_IMPL(uint16);
    case Variant::kUint32Type:
      SERIALIZE_SIMPLE_TYPE_IMPL(uint32);
		case Variant::kInt64Type:
			SERIALIZE_SIMPLE_TYPE_IMPL(int64);
		case Variant::kUint64Type:
			SERIALIZE_SIMPLE_TYPE_IMPL(uint64);
    case Variant::kFloatType:
      SERIALIZE_SIMPLE_TYPE_IMPL(float);
    case Variant::kDoubleType:
      SERIALIZE_SIMPLE_TYPE_IMPL(double);
		case Variant::kStringType:
			SERIALIZE_SIMPLE_TYPE_IMPL(_STD string);
    case Variant::kDurationType:
      SERIALIZE_SIMPLE_TYPE_IMPL(Duration);
    case Variant::kDatetimeType:
      SERIALIZE_SIMPLE_TYPE_IMPL(DateTime);
		case Variant::kObjectIdType:
			SERIALIZE_SIMPLE_TYPE_IMPL(UniqueId);
    }

#undef SERIALIZE_SIMPLE_TYPE_IMPL

    return 0;
  }
};

template <typename Writer, typename _ST = SimpleTypeSerializerTrait<Writer> >
class Serializer {
 public:
  typedef Writer writer_type;

  typedef _ST simple_type_serialize_trait;

  BOOST_STATIC_ASSERT(is_writer<writer_type>::value);

  Serializer(Writer& output) : output_(output) {}

  writer_type& GetWriter() const {
    return output_;
  }

  void Serialize(const Variant& val) {
    Write(val);
  }

  void Flush() {
    output_.Flush();
  }

private:
  bool Write(const Variant& val) {
    if (Variant::IsBuildinTypeId(val.TypeId())) {
      WriteValue(val);
    } else if (val.IsUserType() || val.IsComplexType()) {
      WriteStruct(val);
    } else  if (Variant::kVariantType == val.TypeId()) {
      if (val.TypeFlag() == Variant::kSharedPtrTypeFlag) {
        boost::shared_ptr<Variant>  v = val.Value<boost::shared_ptr<Variant> >();
        Write(*v);
      } else if (val.TypeFlag() == Variant::kValueTypeFlag) {
        Write(val.Value<Variant>());
      }
    }
    return true;
  }

  bool WriteField(const reflection::Property* prop, const Variant& val) {
    output_.WriteFieldBegin(prop);
    Write(val);
    output_.WriteFieldEnd();
    return true;
  }

  bool WriteStruct(const Variant& val) {
		if (val.IsComplexType()) {
			return WriteContainer(val, nullptr);
		}

    if (const reflection::TypeInfo* info = 
			reflection::GetTypeInfo(val.TypeId())) {
			if (const reflection::ExtentInfo* ext = info->extInfo) {
        return WriteContainer(val, ext);
      } else if (Variant::IsUserTypeId(info->id)) {
        const std::vector<const reflection::Property*>& props = 
					reflection::GetProperties(info->id);
        output_.WriteStructBegin(info, (uint32)props.size());
        for (size_t i = 0; i < props.size(); ++i) {
          const reflection::Property* prop = props.at(i);
					if (!WriteField(prop, prop->Get(val.DataPtr()))) {
						return false;
					}
        }
        output_.WriteStructEnd();
				return true;
      }
    }
    return false;
  }

	bool WriteContainer(const Variant& val, const reflection::ExtentInfo* ext = nullptr) {
		if (ext && !val.IsObject()) {
			if (ext->iterator && ext->iterator->IsMapType()) {
				reflection::CollectionEnumerPtr itor =
					ext->iterator->Create(val.DataPtr());
				output_.WriteMapBegin(itor->Size());
				while (itor->HasNext()) {
					_STD pair<Variant, Variant> pi =
						itor->Next().Value<_STD pair<Variant, Variant> >();
					Write(pi.first);
					Write(pi.second);
				}
				output_.WriteMapEnd();
      } else if (ext->iterator && ext->iterator->IsArrayType()) {
				reflection::CollectionEnumerPtr itor =
					ext->iterator->Create(val.DataPtr());
				output_.WriteArrayBegin(itor->Size());
				while (itor->HasNext()) {
					Write(itor->Next());
				}
				output_.WriteArrayEnd();
      }
    } else {
      if (val.IsArray()) {
        uint32 s = val.Size();
        output_.WriteArrayBegin(s);
        Variant** arr = val.GetArray();
        for (uint32 i = 0; i < s; ++i) {
					Write(**(arr + i));
        }
        output_.WriteArrayEnd();
      } else if (val.IsMap()) {
        uint32 s = val.Size();
        output_.WriteMapBegin(s);
        Variant::Pair_T* mp = val.GetMap();
        for (uint32 i = 0; i < s; ++i) {
					Write(*(mp + i)->key);
					Write(*(mp + i)->value);
        }
        output_.WriteMapEnd();
			} else if (val.IsPair()) {
				output_.WritePairBegin();
				Write(*val.GetKey());
				Write(*val.GetValue());
				output_.WritePairEnd();
			}
    }
    return true;
  }

  bool WriteValue(const Variant& val) {
    if (val.IsBuildinType()) {
      return simple_type_serialize_trait::WriteValue(output_, 
				val.TypeId(), val);
    }
    return false;
  }

 protected:
  writer_type& output_;
};


TB_NAMESPACE_END

#endif // TB_REFLECTION_SERIALIZER_H__
